
package com.ttomato.game.framework.impl;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import com.ttomato.game.framework.Audio;
import com.ttomato.game.framework.FileIO;
import com.ttomato.game.framework.Game;
import com.ttomato.game.framework.Graphics;
import com.ttomato.game.framework.Input;
import com.ttomato.game.framework.Screen;

import android.app.Activity;
import android.content.Context;
import android.opengl.GLSurfaceView;
import android.opengl.GLSurfaceView.Renderer;
import android.os.Bundle;
import android.os.PowerManager;
import android.os.PowerManager.WakeLock;
import android.view.Window;
import android.view.WindowManager;

public abstract class GLGame extends Activity implements Game, Renderer {
    enum GLGameState {
        Initialized,
        Running,
        Paused,
        Finished,
        Idle
    }

    GLSurfaceView mGlView;
    GLGraphics mGlGraphics;
    Audio mAudio;
    Input mInput;
    FileIO mFileIO;
    Screen mScreen;
    GLGameState mState = GLGameState.Initialized;
    static Object mStateChanged = new Object();
    long mStartTime = System.nanoTime();
    WakeLock mWakeLock;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        mGlView = new GLSurfaceView(this);
        mGlView.setRenderer(this);
        setContentView(mGlView);

        mGlGraphics = new GLGraphics(mGlView);
        mFileIO = new AndroidFileIO(getAssets());
        mAudio = new AndroidAudio(this);
        mInput = new AndroidInput(this, mGlView, 1, 1);
        PowerManager powerManager = (PowerManager)
                getSystemService(Context.POWER_SERVICE);
        mWakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "GLGame");
    }

    @Override
    protected void onResume() {
        super.onResume();
        mGlView.onResume();
        mWakeLock.acquire();
    }

    @Override
    public void onSurfaceCreated(GL10 gl, EGLConfig config) {
        mGlGraphics.setGl(gl);
        synchronized (mStateChanged) {
            if (mState == GLGameState.Initialized) {
                mScreen = getStartScreen();
            }
            mState = GLGameState.Running;
            mScreen.resume();
            mStartTime = System.nanoTime();
        }
    }

    @Override
    public void onSurfaceChanged(GL10 gl, int width, int height) {

    }

    @Override
    public void onDrawFrame(GL10 gl) {
        GLGameState state = null;
        synchronized (mStateChanged) {
            state = mState;
        }

        if (state == GLGameState.Running) {
            float deltaTime = (System.nanoTime() - mStartTime) / 1000000000.0f;
            mStartTime = System.nanoTime();
            mScreen.update(deltaTime);
            mScreen.present(deltaTime);
        }

        if (state == GLGameState.Paused) {
            mScreen.pause();
            synchronized (mStateChanged) {
                mState = GLGameState.Idle;
                mStateChanged.notifyAll();
            }
        }

        if (state == GLGameState.Paused) {
            mScreen.pause();
            mScreen.dispose();
            synchronized (mStateChanged) {
                mState = GLGameState.Idle;
                mStateChanged.notifyAll();
            }
        }
    }

    @Override
    protected void onPause() {
        synchronized (mStateChanged) {
            if (isFinishing()) {
                mState = GLGameState.Finished;
            } else {
                mState = GLGameState.Paused;
            }

            while (true) {
                try {
                    mStateChanged.wait();
                    break;
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        mWakeLock.release();
        mGlView.onPause();
        super.onPause();
    }

    public GLGraphics getGLGraphics() {
        return mGlGraphics;
    }

    @Override
    public Input getInput() {
        return mInput;
    }

    @Override
    public FileIO getFileIO() {
        return mFileIO;
    }

    @Override
    public Graphics getGraphics() {
        throw new IllegalStateException("We are using OpenGL!");
    }

    @Override
    public Audio getAudio() {
        return mAudio;
    }

    @Override
    public void setScreen(Screen screen) {
        if (null == screen) {
            throw new IllegalArgumentException("Screen must not be null");
        }

        mScreen.pause();
        mScreen.dispose();

        screen.resume();
        screen.update(0);
        mScreen = screen;
    }

    @Override
    public Screen getCurrentScreen() {
        return mScreen;
    }

}
